//---------------------------------------------------------------------------
// Copyright (C) 2005 Maxim Integrated Products, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL MAXIM INTEGRATED PRODUCTS INC. BE LIABLE FOR ANY CLAIM, DAMAGES
// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//---------------------------------------------------------------------------
// EnumAppNote_BF1.C	2-9-06. Changes the include file "MAX3420E.h" to "MAX3420#-BF1.h".
//			To incorporate a bug fix that did not correctly issue a USB stall handshake
//
// This code demonstrates enumeration using the MAXIM MAX3420E USB Peripheral Controller.
// The code handles all host enumeration requests, as well as suspend/resume. The USB device
// implemented with this code reports itself as capable of signaling remote wakeup (RWU), and
// the code supports this operation by checking a RWU pushbutton while the bus is suspended.
//
//  The code requires pushbuttons on GPIN0 and GPIN2. Connect the other side of the pushbutton to GND 
//    (No pullups necessary--they're inside the MAX3420E.)
//  The code supports 4 LEDS connected to the GPOUT pins. These are programmed as 
//    active high, so the LED anodes go to the GPOUT pin and the cathodes go to GND.
//    Use series resistors of about 150 Ohms for VL=3.3V.
//            Pushbuttons                 LED's
//    GPIN0   Send Keystrokes   GPOUT0    Sending Keystrokes
//    GPIN1   ---               GPOUT1    Bus Reset
//    GPIN2   Remote Wakeup     GPOUT2    SUSPEND
//    GPIN3   ---               GPOUT3    Program Loop Active
//
//  The code runs fine without the LEDS or pushbutton (it enumerates)
//  but it requires the pushbuttons to function as a USB device and to signal remote wakeup.
//
//  After enumerating as a USB HID class device, the code implements a one-button keyboard
//  using the button attached to GPIN0. When this button is pressed the program types the 
//  character string defined in "EnumApp_enum_data.h". 
//
//  Because the I/O is implemented using MAX3420E IO pins, I/O code is portable across processors.
//
//  The code enables certain interrupts and the MAX3420E INT pin, but it does not assume any particular
//  uC or compiler for implementing vectored interrupts. It accomplishes this by polling the INT pin for
//  an active low level instead of having the INT pin trigger a particular uC hardware interrupt. This
//  makes the code as generic as possible since it does not assume any particular uC interrupt system
//  or compiler interrupt handler syntax.
//
#include "MAX3420E_BF1.h"       // MAX3420E registers (rREGNAME), bits (bmBITNAME), and some handy macros
#include "EnumApp_enum_data.h"  // HID keyboard enumeration data

typedef unsigned char BYTE;     // these save typing
typedef unsigned short WORD;

// function prototypes
void SPI_Init(void);            // Configure MAXQ2000 and MAX3420E IO pins for SPI
void Reset_MAX(void);           // Reset the MAX3420E
void wreg(BYTE r,BYTE v);       // Write a MAX3420E register byte
void wregAS(BYTE r,BYTE v);     // Same as 'wreg' but also set the ACKSTAT bit in the SPI command byte
BYTE rreg(BYTE r);              // Read a MAX3420E register byte
BYTE rregAS(BYTE r);            // Same as 'rreg' but also set the ACKSTAT bit
void readbytes(BYTE reg, BYTE N, BYTE *p);  // Read N MAX3420E FIFO bytes into the array p
void writebytes(BYTE reg, BYTE N, BYTE *p); // Write N MAX3420E FIFO bytes into the array p
BYTE MAX_Int_Pending(void);     // Poll the MAX3420E INT pin (set for active low level)

// USB functions
void std_request(void);
void class_request(void);
void vendor_request(void);
void send_descriptor(void);
void send_keystroke(BYTE);
void feature(BYTE);
void get_status(void);
void set_interface(void);
void get_interface(void);
void set_configuration(void);
void get_configuration(void);

// Application code
void do_SETUP(void);      // Handle a USB SETUP transfer
void do_IN3(void);        // Send keyboard characters over Endpoint 3-IN
void check_for_resume(void);
void service_irqs(void);
void initialize_MAX(void);

//Global variables
BYTE SUD[8];		// Local copy of the 8 setup data read from the MAX3420E SUDFIFO
BYTE msgidx,msglen;	// Text string in EnumApp_enum_data.h--index and length
BYTE configval;		// Set/Get_Configuration value
BYTE ep3stall;		// Flag for EP3 Stall, set by Set_Feature, reported back in Get_Status
BYTE interfacenum;      // Set/Get interface value
BYTE inhibit_send;	// Flag for the keyboard character send routine
BYTE RWU_enabled;       // Set by Set/Clear_Feature RWU request, sent back for Get_Status-RWU
BYTE Suspended;         // Tells the main loop to look for host resume and RWU pushbutton
WORD msec_timer;        // Count off time in the main loop
WORD blinktimer;        // Count milliseconds to blink the "loop active" light
BYTE send3zeros;        // EP3-IN function uses this to send HID (key up) codes between keystrokes
//
#define ENABLE_IRQS wreg(rEPIEN,(bmSUDAVIE+bmIN3BAVIE)); wreg(rUSBIEN,(bmURESIE+bmURESDNIE));
// Note: the SUSPEND IRQ will be enabled later, when the device is configured.
// This prevents repeated SUSPEND IRQ's

void initialize_MAX(void)
{
ep3stall=0;			// EP3 inintially un-halted (no stall) (CH9 testing)
msgidx = 0;			// start of KB Message[]
msglen = sizeof(Message);     // so we can check for the end of the message
inhibit_send = 0x01;		// 0 means send, 1 means inhibit sending
send3zeros=1;
msec_timer=0;
blinktimer=0;
// software flags
configval=0;                    // at pwr on OR bus reset we're unconfigured
Suspended=0;
RWU_enabled=0;                  // Set by host Set_Feature(enable RWU) request
//
SPI_Init();                     // set up MAXQ2000 to use its SPI port as a master
//
// Always set the FDUPSPI bit in the PINCTL register FIRST if you are using the SPI port in 
// full duplex mode. This configures the port properly for subsequent SPI accesses.
//
wreg(rPINCTL,(bmFDUPSPI+bmINTLEVEL+gpxSOF)); // MAX3420: SPI=full-duplex, INT=neg level, GPX=SOF
Reset_MAX();
wreg(rGPIO,0x00);                   // lites off (Active HIGH)
// This is a self-powered design, so the host could turn off Vbus while we are powered.
// Therefore set the VBGATE bit to have the MAX3420E automatically disconnect the D+
// pullup resistor in the absense of Vbus. Note: the VBCOMP pin must be connected to Vbus
// or pulled high for this code to work--a low on VBCOMP will prevent USB connection.
wreg(rUSBCTL,(bmCONNECT+bmVBGATE)); // VBGATE=1 disconnects D+ pullup if host turns off VBUS
ENABLE_IRQS
wreg(rCPUCTL,bmIE);                 // Enable the INT pin
}

#define TWENTY_MSEC 14200           // adjust this constant for 20 msec button checks
#define BLINKTIME 25                // blink every 500 msec

// ************************************************************************************
// This endless loop checks for two high priority events (every time through the loop):
// 1. USB suspend ("Suspended" flag = 1). If suspended, checks for resume signaling.
// 2. A MAX3420E pending interrupt. 
//
// Every 20 msec, it reads the "SEND" pushbutton. Every half second, it blinks
// the "Loop Active" light.
//
// *********************************** MAIN *******************************************
void main(void)
{
initialize_MAX();
while(1)		// endless loop
  {
  if(Suspended)
    check_for_resume();
  if (MAX_Int_Pending())
    service_irqs();
  msec_timer++;
  if(msec_timer==TWENTY_MSEC)
    {
    msec_timer=0;
    if((rreg(rGPIO) & 0x10) == 0) // Check the pushbutton on GPI-0
        {
        inhibit_send = 0x00;      // Tell the "do_IN3" function to send the text string
        L0_ON                     // Turn on the SEND light
        }
    blinktimer++;                 // blink the loop active light every half second
    if(blinktimer==BLINKTIME)
        {
        blinktimer=0;
        L3_BLINK
        }
    }// msec_timer==ONE_MSEC
  } // while(1)
}// main
      
void check_for_resume(void)         
{
  if(rreg(rUSBIRQ) & bmBUSACTIRQ)     // THE HOST RESUMED BUS TRAFFIC
      {
      L2_OFF
      Suspended=0;                    // no longer suspended
      }
  else if(RWU_enabled)                // Only if the host enabled RWU
      {
      if((rreg(rGPIO)&0x40)==0)       // See if the Remote Wakeup button was pressed
        {
        L2_OFF                        // turn off suspend light
        Suspended=0;                  // no longer suspended
        SETBIT(rUSBCTL,bmSIGRWU)      // signal RWU
        while ((rreg(rUSBIRQ)&bmRWUDNIRQ)==0) ;	// spin until RWU signaling done
        CLRBIT(rUSBCTL,bmSIGRWU)      // remove the RESUME signal 
        wreg(rUSBIRQ,bmRWUDNIRQ);     // clear the IRQ
        while((rreg(rGPIO)&0x40)==0) ;  // hang until RWU button released
        wreg(rUSBIRQ,bmBUSACTIRQ);    // wait for bus traffic -- clear the BUS Active IRQ
        while((rreg(rUSBIRQ) & bmBUSACTIRQ)==0) ; // & hang here until it's set again...
        }
      }
} 
//

void service_irqs(void)
{
BYTE itest1,itest2;
itest1 = rreg(rEPIRQ);            // Check the EPIRQ bits
itest2 = rreg(rUSBIRQ);           // Check the USBIRQ bits
if(itest1 & bmSUDAVIRQ) 
    {
     wreg(rEPIRQ,bmSUDAVIRQ);     // clear the SUDAV IRQ
     do_SETUP();
    }
if(itest1 & bmIN3BAVIRQ)          // Was an EP3-IN packet just dispatched to the host?
    {
    do_IN3();                     // Yes--load another keystroke and arm the endpoint
    }                             // NOTE: don't clear the IN3BAVIRQ bit here--loading the EP3-IN byte
                                  // count register in the do_IN3() function does it.
if((configval != 0) && (itest2&bmSUSPIRQ))   // HOST suspended bus for 3 msec
    {
    wreg(rUSBIRQ,(bmSUSPIRQ+bmBUSACTIRQ));  // clear the IRQ and bus activity IRQ
    L2_ON                         // turn on the SUSPEND light
    L3_OFF                        // turn off blinking light (in case it's on)
    Suspended=1;                  // signal the main loop
    }
if(rreg(rUSBIRQ)& bmURESIRQ)
    {
    L1_ON                         // turn the BUS RESET light on 
    L2_OFF                        // Suspend light off (if on)
    wreg(rUSBIRQ,bmURESIRQ);      // clear the IRQ
    }
if(rreg(rUSBIRQ) & bmURESDNIRQ)
    {
    L1_OFF                        // turn the BUS RESET light off
    wreg(rUSBIRQ,bmURESDNIRQ);    // clear the IRQ bit
    Suspended=0;                  // in case we were suspended
    ENABLE_IRQS                   // ...because a bus reset clears the IE bits
    }
} 

void do_SETUP(void)
{							
readbytes(rSUDFIFO,8,SUD);          // got a SETUP packet. Read 8 SETUP bytes
switch(SUD[bmRequestType]&0x60)     // Parse the SETUP packet. For request type, look only at b6&b5
    {
    case 0x00:	std_request();		break;
    case 0x20:	class_request();	break;  // just a stub in this program
    case 0x40:	vendor_request();	break;  // just a stub in this program
    default:	STALL_EP0                       // unrecognized request type
    }
}

void do_IN3(void)
{
if (inhibit_send==0x01)
        {
	wreg(rEP3INFIFO,0);			// send the "keys up" code
	wreg(rEP3INFIFO,0);
	wreg(rEP3INFIFO,0);
	}
else
  if (send3zeros==0x01)                         // precede every keycode with the "no keys" code
        {
	wreg(rEP3INFIFO,0);			// send the "keys up" code
	wreg(rEP3INFIFO,0);
	wreg(rEP3INFIFO,0);
        send3zeros=0;                           // next time through this function send the keycode
	}
  else
	{
        send3zeros=1;
        wreg(rEP3INFIFO,Message[msgidx++]);	// load the next keystroke (3 bytes)
	wreg(rEP3INFIFO,Message[msgidx++]);
	wreg(rEP3INFIFO,Message[msgidx++]);
	if(msgidx >= msglen)                    // check for message wrap
            {
            msgidx=0;
            L0_OFF
            inhibit_send=1;                     // send the string once per pushbutton press
            }
	}
wreg(rEP3INBC,3);				// arm it
}

//*******************
void std_request(void)
{
switch(SUD[bRequest])			
	{
	case	SR_GET_DESCRIPTOR:	send_descriptor();    break;
	case	SR_SET_FEATURE:		feature(1);           break;
	case	SR_CLEAR_FEATURE:	feature(0);           break;
	case	SR_GET_STATUS:		get_status();         break;
	case	SR_SET_INTERFACE:	set_interface();      break;
	case	SR_GET_INTERFACE:	get_interface();      break;
	case	SR_GET_CONFIGURATION:   get_configuration();  break;
	case	SR_SET_CONFIGURATION:   set_configuration();  break;
	case	SR_SET_ADDRESS:         rregAS(rFNADDR);      break;  // discard return value
	default:  STALL_EP0
	}
}

//**************************
void set_configuration(void)
{
configval=SUD[wValueL];           // Store the config value
if(configval != 0)                // If we are configured, 
  SETBIT(rUSBIEN,bmSUSPIE);       // start looking for SUSPEND interrupts
rregAS(rFNADDR);                  // dummy read to set the ACKSTAT bit
}

void get_configuration(void)
{
wreg(rEP0FIFO,configval);         // Send the config value
wregAS(rEP0BC,1);   
}

//**********************
void set_interface(void)	// All we accept are Interface=0 and AlternateSetting=0, otherwise send STALL
{
BYTE dumval;
if((SUD[wValueL]==0)		// wValueL=Alternate Setting index
  &&(SUD[wIndexL]==0))		// wIndexL=Interface index
  	dumval=rregAS(rFNADDR);	// dummy read to set the ACKSTAT bit
else STALL_EP0
}

//**********************
void get_interface(void)	// Check for Interface=0, always report AlternateSetting=0
{
if(SUD[wIndexL]==0)		// wIndexL=Interface index
  {
  wreg(rEP0FIFO,0);		// AS=0
  wregAS(rEP0BC,1);		// send one byte, ACKSTAT
  }
else STALL_EP0
}

//*******************
void get_status(void)
{
BYTE testbyte;
testbyte=SUD[bmRequestType];
switch(testbyte)	
	{
	case 0x80: 			// directed to DEVICE
		wreg(rEP0FIFO,(RWU_enabled+1));	// first byte is 000000rs where r=enabled for RWU and s=self-powered.
		wreg(rEP0FIFO,0x00);		// second byte is always 0
		wregAS(rEP0BC,2); 		// load byte count, arm the IN transfer, ACK the status stage of the CTL transfer
		break; 				
	case 0x81: 			// directed to INTERFACE
		wreg(rEP0FIFO,0x00);		// this one is easy--two zero bytes
		wreg(rEP0FIFO,0x00);		
		wregAS(rEP0BC,2); 		// load byte count, arm the IN transfer, ACK the status stage of the CTL transfer
		break; 				
	case 0x82: 			// directed to ENDPOINT
		if(SUD[wIndexL]==0x83)		// We only reported ep3, so it's the only one the host can stall IN3=83
                  {
                  wreg(rEP0FIFO,ep3stall);	// first byte is 0000000h where h is the halt (stall) bit
                  wreg(rEP0FIFO,0x00);		// second byte is always 0
                  wregAS(rEP0BC,2); 		// load byte count, arm the IN transfer, ACK the status stage of the CTL transfer
                  break;
                  }
		else  STALL_EP0		// Host tried to stall an invalid endpoint (not 3)				
	default:      STALL_EP0		// don't recognize the request
	}
}

// **********************************************************************************************
// FUNCTION: Set/Get_Feature. Call as feature(1) for Set_Feature or feature(0) for Clear_Feature.
// There are two set/clear feature requests:
//	To a DEVICE: 	Remote Wakeup (RWU). 
//  	To an ENDPOINT:	Stall (EP3 only for this app)
//
void feature(BYTE sc)
{
BYTE mask;
  if((SUD[bmRequestType]==0x02)	// dir=h->p, recipient = ENDPOINT
  &&  (SUD[wValueL]==0x00)	// wValueL is feature selector, 00 is EP Halt
  &&  (SUD[wIndexL]==0x83))	// wIndexL is endpoint number IN3=83
      {
      mask=rreg(rEPSTALLS);   // read existing bits
      if(sc==1)               // set_feature
        {
        mask += bmSTLEP3IN;       // Halt EP3IN
        ep3stall=1;
        }
      else                        // clear_feature
        {
        mask &= ~bmSTLEP3IN;      // UnHalt EP3IN
        ep3stall=0;
        wreg(rCLRTOGS,bmCTGEP3IN);  // clear the EP3 data toggle
        }
      wreg(rEPSTALLS,(mask|bmACKSTAT)); // Don't use wregAS for this--directly writing the ACKSTAT bit
      }
  else if ((SUD[bmRequestType]==0x00)	// dir=h->p, recipient = DEVICE
           &&  (SUD[wValueL]==0x01))	// wValueL is feature selector, 01 is Device_Remote_Wakeup
            {
            RWU_enabled = sc<<1;	// =2 for set, =0 for clear feature. The shift puts it in the get_status bit position.			
            rregAS(rFNADDR);		// dummy read to set ACKSTAT
            }
  else STALL_EP0
}

//************************
void send_descriptor(void)
{
WORD reqlen,sendlen,desclen;
BYTE *pDdata;					// pointer to ROM Descriptor data to send
//
// NOTE This function assumes all descriptors are 64 or fewer bytes and can be sent in a single packet
//
desclen = 0;					// check for zero as error condition (no case statements satisfied)
reqlen = SUD[wLengthL] + 256*SUD[wLengthH];	// 16-bit
	switch (SUD[wValueH])			// wValueH is descriptor type
	{
	case  GD_DEVICE:
              desclen = DD[0];	// descriptor length
              pDdata = DD;
              break;	
	case  GD_CONFIGURATION:
              desclen = CD[2];	// Config descriptor includes interface, HID, report and ep descriptors
              pDdata = CD;
              break;
	case  GD_STRING:
              desclen = strDesc[SUD[wValueL]][0];   // wValueL=string index, array[0] is the length
              pDdata = strDesc[SUD[wValueL]];       // point to first array element
              break;
	case  GD_HID:
              desclen = CD[18];
              pDdata = &CD[18];
              break;
	case  GD_REPORT:
              desclen = CD[25];
              pDdata = RepD;
        break;
	}	// end switch on descriptor type
//
if (desclen!=0)                   // one of the case statements above filled in a value
	{
	sendlen = (reqlen <= desclen) ? reqlen : desclen; // send the smaller of requested and avaiable
        writebytes(rEP0FIFO,sendlen,pDdata);
	wregAS(rEP0BC,sendlen);   // load EP0BC to arm the EP0-IN transfer & ACKSTAT
	}
else STALL_EP0  // none of the descriptor types match
}

void class_request(void) 
{
STALL_EP0
}                         

void vendor_request(void)
{
STALL_EP0
}

// ******************** END of ENUMERATION CODE ********************
//
void Reset_MAX(void)	
{
BYTE dum;
wreg(rUSBCTL,0x20);	// chip reset
wreg(rUSBCTL,0x00);	// remove the reset
    do                  // Chip reset stops the oscillator. Wait for it to stabilize.
    {
    dum=rreg(rUSBIRQ);
    dum &= bmOSCOKIRQ;
    }
    while (dum==0);
}
//
// ------------------------------------------------------------
// The code below customizes this app for the MAXQ2000
// microprocessor and the Rowley compiler. Only this
// section changes if you use a different uP and/or compiler.
// ------------------------------------------------------------
//
// The MAX3420E is wired to the MAXQ2000 as follows:
//
// MISO P57 (in)
// SCLK P56 (out)
// MOSI P55 (out)
// SS#  P54 (out)
// GPX  P53 (in)
// INT  P60 (in) 

sfrw SPIB =   0x53;      // This is how the Rowley compiler declares MAXQ SFR registers
sfrb SPICF =  0x163;
sfrb SPICK =  0x173;
sfrb CKCN =   0xe8;
sfrb PO5 =    0x11;
sfrb PD5 =    0x111;
sfrb PD6 =    0x121;
sfrb PI6 =    0xA1;
sfrb SPICN =  0x153;

// Register SPICN bit masks
#define bmSPIEN 0x01
#define bmMSTM  0x02
#define bmSTBY  0x80
//
#define SS_HI PO5 |= 0x10;    // SS# connected to Port5 bit4 in this app
#define SS_LO PO5 &= ~0x10; 

BYTE MAX_Int_Pending(void)
{
return (BYTE)((PI6&0x01)==0);
}

void SPI_Init(void)
{
// Set up the MAXQ2000 SPI port
  CKCN = 0x00;              // system clock divisor is 1
  SS_HI                     // SS# high  
  PD5 |= 0x070;             // Set SPI pins (SCLK, MOSI, and SS#) as outputs
  PD5 &= ~0x088;            // Set SPI pins (MISO,GPX) as inputs
  PD6 &= ~0x01;             // Set P60 (INT) as input
  SPICK = 0x00;             // fastest SPI clock--div by 2 
  SPICF = 0x00;             // mode(0,0), 8 bit data
  SPICN |= bmMSTM;          // Set SPI controller as master
  SPICN |= bmSPIEN;         // Enable the SPI controller
}

void wreg(BYTE reg, BYTE dat)
{
  SS_LO                     // Set SS# low
  SPIB = reg+2;             // send the register number with the DIR bit (b1) set to WRITE
  while (SPICN & bmSTBY);   // loop if data still being sent
  SPIB = dat;               // send the data
  while (SPICN & bmSTBY);   // loop if data still being sent
  SS_HI                     // set SS# high
}


// Write a MAX3410E register with the "ACK STATUS" bit set in the command byte
void wregAS(BYTE reg, BYTE dat)
{
  SS_LO                     // Set SS# low
  SPIB = reg+3;             // reg number with DIR=1 (write) and ACKSTAT=1
  while (SPICN & bmSTBY);   // loop if data still being sent
  SPIB = dat;               // send the data
  while (SPICN & bmSTBY);   // loop if data still being sent
  SS_HI                     // set SS# high
}

// Read a register, return its value.
BYTE rreg(BYTE reg)
{
BYTE dum;
  SS_LO
  SPIB = reg;               // reg number w. dir=0 (IN)
  while (SPICN & bmSTBY);   // loop if data still being sent
  dum = SPIB;               // NECESSARY TO RE-ENABLE THE INPUT BUFFER in BYTE MODE
  SPIB=0x00;                // data is don't care, we're clocking in MISO bits
  while (SPICN & bmSTBY);   // loop if data still being sent
  SS_HI
  return(SPIB);
}

// Read a byte (as rreg), but also set the AckStat bit in the command byte.
BYTE rregAS(BYTE reg)
{
BYTE dum;
  SS_LO
  SPIB = reg+1;             // reg number w. dir=0 (IN) and ACKSTAT=1
  while (SPICN & bmSTBY);   // loop if data still being sent
  dum = SPIB;               // NECESSARY TO RE-ENABLE THE INPUT BUFFER in BYTE MODE
  SPIB=0xFF;                // data is don't care, we're clocking in MISO bits
  while (SPICN & bmSTBY);   // loop if data still being sent
  SS_HI
  return(SPIB);
}

void readbytes(BYTE reg, BYTE N, BYTE *p)
{
BYTE j;
  SS_LO
  SPIB = reg;               // write bit b1=0 to command a read operation
  while (SPICN & bmSTBY);   // loop if data still being sent
  j = SPIB;                 // NECESSARY TO RE-ENABLE THE INPUT BUFFER in BYTE MODE
  for(j=0; j<N; j++)
    {
    SPIB = 0x00;            // dummy value to get the next read byte
  while (SPICN & bmSTBY);   // loop if data still being sent
    *p = SPIB;              // store it in the data array
    p++;                    // bump the pointer
    }
  SS_HI
}
void writebytes(BYTE reg, BYTE N, BYTE *p)
{
BYTE j,wd;
  SS_LO
  SPIB = reg+2;             // write bit b1=1 to command a write operation
  while (SPICN & bmSTBY);   // loop if data still being sent
  for(j=0; j<N; j++)
    {
    wd = *p;                // write the array value
    SPIB = wd;
  while (SPICN & bmSTBY);   // loop if data still being sent
    p++;                    // bump the pointer
    }
  SS_HI
}
//
// Diagnostic Aid:
// Call this function from main() to verify operation of your SPI port.
//
void test_SPI(void)         // Use this to check your versions of the rreg and wreg functions
{
BYTE j,wr,rd;
SPI_Init();                 // Configure and initialize the uP's SPI port
wreg(rPINCTL,bmFDUPSPI);    // MAX3420: SPI=full-duplex
wreg(rUSBCTL,bmCHIPRES);    // reset the MAX3420E
wreg(rUSBCTL,0);            // remove the reset
wr=0x01;                    // initial register write value
for(j=0; j<8; j++)
  {
  wreg(rUSBIEN,wr);
  rd = rreg(rUSBIEN);           
  wr <<= 1;       // Put a breakpoint here. Values of 'rd' should be 01,02,04,08,10,20,40,80
  }
}
 
